﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Windows.Forms;
using mshtml;

namespace WebHelper
{
    class WebClientWithSession : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            var request = base.GetWebRequest(address);
           
            if (request is HttpWebRequest)
            {
                ((HttpWebRequest)request).CookieContainer = container;
                ((HttpWebRequest)request).KeepAlive = false;
            }

            return request;
        }

        public string Get(string url)
        {
            Headers.Remove("Content-Type");
            return (new System.IO.StreamReader(OpenRead(url))).ReadToEnd();
        }

        public string PostForm(string url, string postquery, string encode)
        {
            Headers.Add("Content-Type", "application/x-www-form-urlencoded; charset=" + encode);
            byte[] data = Encoding.GetEncoding(encode).GetBytes(postquery);
            byte[] responseData = UploadData(url, "POST", data);
            return (new System.IO.StreamReader(new System.IO.MemoryStream(responseData))).ReadToEnd();
        }

        public void Reset()
        {
            container = new CookieContainer();
        }

        CookieContainer container = new CookieContainer();
    }

    class WebHelper
    {
        public bool OpenSession()
        {
            browser.CreateControl();
            browser.Navigate(new Uri( _domain));
            System.Threading.Thread.Sleep(5000);
            return true;
        }

        WebBrowser browser = new WebBrowser();
        string _domain = "http://web.oa.wanmei.com/";
        string _ssoDomain = "http://sso.oa.wanmei.com/";
        string _username = "liuhandong";
        string _passwd = "se19880529";
    }

    class HolidayHelper
    {
        private void FindHolidayURL(int year)
        {
            string search = string.Format(searchurl, year-1, year);
            string content = _client.Get(search);
            System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("<li class=\"res-list\">\\s*<h3 class=\"res-sub-title\"><a href=\"([^\"]+)\"");
            var match = reg.Match(content);
            if(match.Length > 1)
            {
                url = match.Groups[1].Value;
            }
        }

        public void FindHoliday(int year)
        {
            FindHolidayURL(year);
            FindHoliday();
        }

        public bool IsHoliday(int year, int month, int day)
        {
            foreach(var hol in holidays)
            {
                if(hol.Year == year && hol.Month == month && hol.Day == day)
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsWorkday(int year, int month, int day)
        {
            foreach (var hol in workdays)
            {
                if (hol.Year == year && hol.Month == month && hol.Day == day)
                {
                    return true;
                }
            }
            return false;
        }

        public void FindHoliday()
        {
            if(url != "")
            {
                string content = "";
                try
                {
                    content = _client.Get(url);
                }
                catch(System.Exception ex)
                {
                    //do nothing
                }
                System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("国务院办公厅关于(\\d+)年部分节假日安排的通知");
                var match = reg.Match(content);
                if (match.Length > 1)
                    _year = int.Parse(match.Groups[1].Value);
                var hi = GetLines(content);
                foreach(var h in hi)
                {
                    holidays.AddRange(h.ParseHolidays(_year));
                    workdays.AddRange(h.ParseWorkDays(_year));
                }
            }
        }

        class DateParser
        {
            public delegate List<DateTime> ParseFunc(System.Text.RegularExpressions.Match match, int year, int beginGroup);
            public ParseFunc parseFunc;
            public string pattern;
            public int groups;
        }

        class HolidayInfomation
        {
            public string name;
            public string line;
            static List<DateTime> GetSingleMonthDay(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                res.Add(new DateTime(year, int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup+1].Value)));
                return res;
            }

            static List<DateTime> GetSingleYearMonthDay(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                res.Add(new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup+1].Value), int.Parse(match.Groups[beginGroup+2].Value)));
                return res;
            }

            static List<DateTime> GetSameMonthMultiDay(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                var start = new DateTime(year, int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 1].Value));
                var end = new DateTime(year, int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 2].Value));
                while(start.CompareTo(end)<0)
                {
                    res.Add(new DateTime(start.Year, start.Month, start.Day));
                    start = start.AddDays(1);
                }
                res.Add(end);
                return res;
            }

            static List<DateTime> GetDifferentMonthMultiDay(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                var start = new DateTime(year, int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 1].Value));
                var end = new DateTime(year, int.Parse(match.Groups[beginGroup+2].Value), int.Parse(match.Groups[beginGroup + 3].Value));
                while (start.CompareTo(end) < 0)
                {
                    res.Add(new DateTime(start.Year, start.Month, start.Day));
                    start = start.AddDays(1);
                }
                res.Add(end);
                return res;
            }

            static List<DateTime> GetDifferentYearMonthMultiDay(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                var start = new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup+1].Value), int.Parse(match.Groups[beginGroup + 2].Value));
                var end = new DateTime(int.Parse(match.Groups[beginGroup].Value + 3), int.Parse(match.Groups[beginGroup + 4].Value), int.Parse(match.Groups[beginGroup + 5].Value));
                while (start.CompareTo(end) < 0)
                {
                    res.Add(new DateTime(start.Year, start.Month, start.Day));
                    start = start.AddDays(1);
                }
                res.Add(end);
                return res;
            }

            static List<DateTime> GetDifferentMonthMultiDayHasYear(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                var start = new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 1].Value), int.Parse(match.Groups[beginGroup + 2].Value));
                var end = new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 3].Value), int.Parse(match.Groups[beginGroup + 4].Value));
                while (start.CompareTo(end) < 0)
                {
                    res.Add(new DateTime(start.Year, start.Month, start.Day));
                    start = start.AddDays(1);
                }
                res.Add(end);
                return res;
            }

            static List<DateTime> GetMultiDayHasYearMonth(System.Text.RegularExpressions.Match match, int year, int beginGroup)
            {
                var res = new List<DateTime>();
                var start = new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 1].Value), int.Parse(match.Groups[beginGroup + 2].Value));
                var end = new DateTime(int.Parse(match.Groups[beginGroup].Value), int.Parse(match.Groups[beginGroup + 1].Value), int.Parse(match.Groups[beginGroup + 3].Value));
                while (start.CompareTo(end) < 0)
                {
                    res.Add(new DateTime(start.Year, start.Month, start.Day));
                    start = start.AddDays(1);
                }
                res.Add(end);
                return res;
            }

            DateParser[] dateParser = new DateParser[]
            {
                new DateParser{pattern = @"[^年至\d](\d+)月(\d+)日", parseFunc = GetSingleMonthDay, groups = 2},
                new DateParser{pattern = @"[^年至\d](\d+)月(\d+)日（[^）]+）", parseFunc = GetSingleMonthDay, groups = 2},
                new DateParser{pattern = @"[^至](\d+)年(\d+)月(\d+)日", parseFunc = GetSingleYearMonthDay, groups = 3},
                new DateParser{pattern = @"[^至](\d+)年(\d+)月(\d+)日（[^）]+）", parseFunc = GetSingleYearMonthDay, groups = 3},
                new DateParser{pattern = @"[^年至\d](\d+)月(\d+)日至(\d+)日", parseFunc = GetSameMonthMultiDay, groups = 3},
                new DateParser{pattern = @"[^年至\d](\d+)月(\d+)日至(\d+)月(\d+)日", parseFunc = GetDifferentMonthMultiDay, groups = 4},
                new DateParser{pattern = @"[^至](\d+)年(\d+)月(\d+)日至(\d+)年(\d+)月(\d+)日", parseFunc = GetDifferentYearMonthMultiDay, groups = 6},
                new DateParser{pattern = @"[^至](\d+)年(\d+)月(\d+)日至(\d+)月(\d+)日", parseFunc = GetDifferentMonthMultiDayHasYear, groups = 5},
                new DateParser{pattern = @"[^至](\d+)年(\d+)月(\d+)日至(\d+)日", parseFunc = GetMultiDayHasYearMonth, groups = 4},
            };

            string[] holidayPostfix = new string[] { "放假", "补休" };
            string[] workdayPostfix = new string[] { "上班" };



            public List<DateTime> ParseHolidays(int year)
            {
                List<DateTime> res = new List<DateTime>();
                foreach(var dates in dateParser)
                {
                    foreach(var postfix in holidayPostfix)
                    {
                        for (int dateRepeat = 0; dateRepeat < 3; dateRepeat++)
                        {
                            string dateString = dates.pattern;
                            for(int i = 0; i < dateRepeat; i++ )
                            {
                                dateString += "、" + dates.pattern;
                            }
                            System.Text.RegularExpressions.Regex pattern = new System.Text.RegularExpressions.Regex(dateString + postfix);
                            var matches = pattern.Matches(line);
                            foreach(var m in matches)
                            {
                                var match = m as System.Text.RegularExpressions.Match;
                                if (match.Groups.Count < 1 + dateRepeat * dates.groups + dates.groups)
                                    continue;
                                for(int i = 0; i <= dateRepeat; i++)
                                {
                                    res.AddRange(dates.parseFunc(match, year, 1 + i * dates.groups));
                                }
                            }
                        }
                    }
                }

                if (line.IndexOf("与周末连休") >= 0)
                {
                    //include neibour weekend
                    for(int i = 0; i < res.Count; i++)
                    {
                        if(res[i].DayOfWeek == DayOfWeek.Friday)
                        {
                            res.Add(res[i].AddDays(1));
                            res.Add(res[i].AddDays(2));
                            break;
                        }
                        else if(res[i].DayOfWeek == DayOfWeek.Monday)
                        {
                            res.Add(res[i].AddDays(-1));
                            res.Add(res[i].AddDays(-2));
                            break;
                        }
                    }
                }
                return res;
            }

            public List<DateTime> ParseWorkDays(int year)
            {
                
                List<DateTime> res = new List<DateTime>();
                foreach(var dates in dateParser)
                {
                    foreach(var postfix in workdayPostfix)
                    {
                        for (int dateRepeat = 0; dateRepeat < 3; dateRepeat++)
                        {
                            string dateString = dates.pattern;
                            for(int i = 0; i < dateRepeat; i++ )
                            {
                                dateString += "、" + dates.pattern;
                            }
                            System.Text.RegularExpressions.Regex pattern = new System.Text.RegularExpressions.Regex(dateString + postfix);
                            var matches = pattern.Matches(line);
                            foreach(var m in matches)
                            {
                                var match = m as System.Text.RegularExpressions.Match;
                                if (match.Groups.Count < 1 + dateRepeat * dates.groups + dates.groups)
                                    continue;
                                if (match.Value.Trim()[0] == '囧')
                                    continue;
                                for(int i = 0; i <= dateRepeat; i++)
                                {
                                    res.AddRange(dates.parseFunc(match, year, 1 + i * dates.groups));
                                }
                            }
                        }
                    }
                }
                return res;
            }
        }

        private HolidayInfomation[] GetLines(string content)
        {
            content = content.Replace("<br/>", "\n");
            System.Text.RegularExpressions.Regex line = new System.Text.RegularExpressions.Regex("<strong>[一二三四五六七八九十]+、([^：]*)：</strong>([^\n]+)\n");
            var matches = line.Matches(content);
            List<HolidayInfomation> res = new List<HolidayInfomation>();
            foreach(var match in matches)
            {
                res.Add(new HolidayInfomation(){ name = ((System.Text.RegularExpressions.Match) match).Groups[1].Value, line = "头" + ((System.Text.RegularExpressions.Match) match).Groups[2].Value});
                res[res.Count - 1].line = res[res.Count - 1].line.Replace("、", "、囧");
            }
            return res.ToArray();
        }


        int _year;
        WebClientWithSession _client = new WebClientWithSession();
        private List<DateTime> holidays = new List<DateTime>();
        private List<DateTime> workdays = new List<DateTime>();
        private string url = "";
        private string searchurl = "http://new.sousuo.gov.cn/s.htm?t=paper&advance=false&n=10&sort=&timetype=timezd&mintime={0}-01-01&maxtime={0}-12-31&q=%E5%9B%BD%E5%8A%A1%E9%99%A2%E5%8A%9E%E5%85%AC%E5%8E%85%E5%85%B3%E4%BA%8E{1}%E5%B9%B4%E9%83%A8%E5%88%86%E8%8A%82%E5%81%87%E6%97%A5%E5%AE%89%E6%8E%92%E7%9A%84%E9%80%9A%E7%9F%A5";
    }

    class WebHelper2
    {
        public bool OpenSession()
        {
            client.Reset();
            _hasSignedIn = false;
            _hasSignedOut = false;
            _logInSuccess = false;
            string content = client.Get(_domain) ;
            //login
            var doc = new HTMLDocumentClass();
            ((IHTMLDocument2)doc).write(content);
            string[] hiddenForLogin = new string[]
            {
                "target",
                "smauthreason",
                "postpreservationdata",
                "smagentname",
            };
            string query = GenerateQueryString("", hiddenForLogin, doc);
            query = GenerateQueryString(query, new string[] { "loginType", "USER", "PASSWORD" }, new string[] { "form", _username, _passwd });
            string response = client.PostForm(_ssoDomain + "PWForms/loginForm/HtmlLogin.jsp", query, "UTF-8");
            query = GenerateQueryString(query, new string[] { "SMSAVECREDS" }, new string[] { "null" });
            response = client.PostForm(_ssoDomain + "siteminderagent/forms/savecreds.fcc", query, "UTF-8");

            System.Text.RegularExpressions.Regex wrongpassword = new System.Text.RegularExpressions.Regex("User Name or Password not correct.The numbers you have tried:(\\d+)");
            var match = wrongpassword.Match(response);
            if(match.Length > 0)
            {
                if(match.Length > 1)
                {
                    WrongPasswordTryTimes = int.Parse(match.Groups[1].Value);
                }
                return false;
            }
            else
            {
                _logInSuccess = true;
            }

            response = client.Get(_domain);
            response = client.Get(_domain + "Pages/index.htm");
            GetSignStatus();
            return true;
        }

        //if successfully logined, return 0;
        public int GetWrongPasswordTryTimes()
        {
            return _logInSuccess? 0: WrongPasswordTryTimes;
        }

        public bool DoSignIn()
        {
            if (_hasSignedIn)
                return true;
            if (!_logInSuccess)
                return false;
            string query = GenerateQueryString("", new string[] { "method" }, new string[] { "DoSignIn" });
            string response = client.PostForm(_domain + "OaHomePage/Handler/signInOut.ashx", query, "UTF-8");
            return true;
        }

        public bool DoSignOut(bool force)
        {
            if (!force && _hasSignedOut)
                return true;
            if (!_logInSuccess)
                return false;
            string query = GenerateQueryString("", new string[] { "method" }, new string[] { "DoSignOut" });
            string response = client.PostForm(_domain + "OaHomePage/Handler/signInOut.ashx", query, "UTF-8");
            return true;
        }

        public void GetSignStatus()
        {
            if (!_logInSuccess)
                return;
            string query = GenerateQueryString("", new string[] { "method" }, new string[] { "GetSignInfo" });
            string response = client.PostForm(_domain + "OaHomePage/Handler/signInOut.ashx", query, "UTF-8");
            System.Text.RegularExpressions.Regex regexForSignStatus = new System.Text.RegularExpressions.Regex("actionType:'(\\w+)'");
            System.Text.RegularExpressions.Match match = regexForSignStatus.Match(response);
            if(match.Length > 1)
            {
                string status = match.Groups[1].Value;
                if (status != "normalNoData" && status != "machineNoData")
                    _hasSignedIn = true;
                if (status == "hasOffTime")
                    _hasSignedOut = true;
            }
        }

        public void SetUserNamePassWD(string un, string pwd)
        {
            _username = un;
            _passwd = pwd;
        }

        string GenerateQueryString(string pre, string[] name, HTMLDocumentClass doc)
        {
            List<string> values = new List<string>();
            foreach (var nm in name)
            {
                var elems = doc.getElementsByName(nm);
                if (elems.length <= 0)
                    return pre;
                else
                {
                    IHTMLElement elem = elems.item(0) as IHTMLElement;
                    object val = elem.getAttribute("value");
                    values.Add((string)val);
                    //values.Add(elem.getAttribute("value").ToString());
                }
            }
            return GenerateQueryString(pre, name, values.ToArray());
        }

        string GenerateQueryString(string pre, string[] name, string[] value)
        {
            if (name != null && value != null && name.Length > 0 && name.Length == value.Length)
            {
                for (int i = 0; i < name.Length; i++)
                {
                    if(i == 0 && (pre == null || pre.Trim() == ""))
                        pre += System.Web.HttpUtility.UrlEncode(name[i]) + "=" + System.Web.HttpUtility.UrlEncode(value[i]);
                    else
                        pre += "&" + System.Web.HttpUtility.UrlEncode(name[i]) + "=" + System.Web.HttpUtility.UrlEncode(value[i]);
                }
            }
            return pre;
        }

        public string GetUserName()
        {
            return _username;
        }

        public string GetPwd()
        {
            return _passwd;
        }

        WebClientWithSession client = new WebClientWithSession();
        string _domain = "http://web.oa.wanmei.com/";
        string _ssoDomain = "http://sso.oa.wanmei.com/";
        string _username = "";
        string _passwd = "";
        bool _hasSignedIn = false;
        bool _hasSignedOut = false;
        bool _logInSuccess = false;
        int WrongPasswordTryTimes = 0;
    }
}
